iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 29
1

之前的 memoize 快取函數是簡單閉包(Closure)的應用方式, 你可以試試動手做, 幫 memoize 函數加上快取過期時間, 或者觸發回應快取內容變更事件, 甚至增加建立快取使用不同的儲存媒體方式儲存.

裝飾器 Decorators

Typescript 的裝飾器Decorators 就像C# 的Attribute , 它可以實現AOP 面向切面編程開發, 不修改原來的程式碼的前提下, 給原來的程式碼增加額外的功能.

想要用Typescript 裝飾器, 必須修改tsconfig.json 參數如下

{
   "compilerOptions": {
      "target": "es5",
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true
   }
}

首先Typescript 建立裝飾器的方式必須是回傳函數, 以下是方法裝飾器的典型範例

function CustomDecorator() {
   return (target: object, methodName: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor => {
      ...
      return descriptor;
   };
}

方法裝飾器函數提供三個參數

  • target 類別(Class) 的原型定義或是方法的構造函數
  • methodName 成員函數的名稱
  • descriptor 成員的屬性描述

首先我們來建立快取(Cache) 裝飾器, 以下示範我們是如何實作的

function CacheDecorator() {
   return (target: object, methodName: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor => {
      if (!descriptor.hasOwnProperty('set') && descriptor.value) {
         descriptor.value = createCacheDecorator(target, descriptor.value);
      } else {
         throw new Error(`Can't set cache decorator on a non-method`);
      }
      return descriptor;
   };
}

首先CacheDecorator 裡面檢查成員名稱是否為非 'set' 開頭, 並且是否有提供 method 資訊

if (!descriptor.hasOwnProperty('set') && descriptor.value) {
   ...
}

如果是的話, 就建立新的方法取代原本物件的方法定義

descriptor.value = createCacheDecorator(target, descriptor.value);

如同之前的 memoize 函數, 同樣地我們建立 CacheDecorator 閉包函數

function createCacheDecorator(target: object, method: (...args) => any): () => any {
   const cache: {} = {};
   return function(...args: any[]): any {
      const argsKey: string = JSON.stringify(args);
      if( !cache.hasOwnProperty(argsKey) ){
         const res = method.call(this, ...args);
         cache[argsKey] = res;
         return res;
      }
      return cache[argsKey];
   };
}

然後我們用新的 CacheDecorator 閉包函數取代物件中原本的方法.

descriptor.value = createCacheDecorator(target, descriptor.value);

假設你企圖在物件裡, 用我們實作的 CacheDecorator 裝飾器類別, 掛載在屬性上

class SomeClass {
   @CacheDecorator()
   id: number = 1;
}

Typescript 將會顯示以下錯誤訊息

Unable to resolve signature of property decorator when called as an expression.

現在我們可以在任何物件中的任何方法, 掛載這個 CacheDecorator 裝飾器

class User {
   @CacheDecorator()
   add(a: number, b: number): number {
      return a + b;
   }
}

這是多麼酷炫的應用, 可以讓我們不改變已現有的程式碼內容, 再加上具有快取(Cache) 的功能.

物件導向的 "多重繼承" 解

Typescript 的物件類別只能單一繼承, 因此你不能這樣做

class Plyaer extends Human, Animal {
    ...
}

Typescript 會告知你, 物件不能夠多重繼承

Classes can only extend a single class.

但我們可以利用另一種方式來建構它們, 這種方式稱為混合(Mixins).

首先必須建立建構元類型

type Constructor<T = {}> = new (...args: any[]) => T;

然後我們提供一個為某個物件類型增加 address 屬性的方法

function AddAddress<TBase extends Constructor>(base: TBase) {
    return class extends base {
        address: string = "";
    }
}

AddAddress 方法會另外產生新的物件類型並繼承 base, 再回傳這個新的物件類型.

於是我們就可以這樣使用... 幫 User 類型增加 address 屬性

class User {
    name: string = "flash";
}

const AddressUser = AddAddresses(User);

let obj = new AddressUser();
console.log(obj.address);

同樣地我們也能為物件類別增加方法

function AddSayHello<TBase extends Constructor>(base: TBase) {
    return class extends base {
        sayHello(msg: string): void {
            console.log("hello " + msg);
        }
    }
}

const UserCanSayHello = AddSayHello(User);

let obj = new UserCanSayHello();
console.log(obj.sayHello());

上一篇
快取柯里化 - 28
下一篇
結尾 - 30
系列文
為什麼世界需要Typescript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
tytom2003
iT邦新手 5 級 ‧ 2020-05-26 20:09:50

因為想學TYPESCRIPT Decoraotr,想問一問tsconfig.json 應該儲存在那裏? 是不是儲存在"C:\Program Files\nodejs\node_modules\npm"裏面? THX

應該要放在你的專案根目錄底下

我要留言

立即登入留言